/*
 * service_site_manager.h
 *
 *  Created on: 2024-06-14
 *      Author: van
 */

#ifndef LIB_SERVICE_SITE_MANAGER_H_
#define LIB_SERVICE_SITE_MANAGER_H_

#include <iostream>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <string>
#include <vector>
#include <array>

#include "nlohmann/json.hpp"

#include "http/httplib.h"
#include "lock_utils.h"

#include "service_site_lib_ojm.h"

#if defined(__has_include) && __has_include("service_site_manager_opt.h")
//用于库的测试, 或者App希望能改写部分参数的场合
#include "service_site_manager_opt.h"
#else
//超出SITE_CFG_MAX_SEND_RETRY次连续失败, 将进入长期离线的快速路径, 失败一次
#define SITE_CFG_MAX_SEND_RETRY 30
//订阅消息的发送队列的最大长度
#define SITE_CFG_TX_QUEUE_SIZE 20
#define SITE_CFG_TX_QUEUE_SIZE_HIGH_PRIORITY 200
//发送失败后的最大重发次数
#define SITE_CFG_SEND_CNT 1
//超出SITE_CFG_MAX_SEND_RETRY次连续失败, 且大于等于SITE_CFG_SUSPEND_TIME秒未传输成功的将被置为SUSPEND状态
//NOTE: 仅在发送新消息时检测时间戳是否过期, 重启时会加载之前的时间戳, 但不进行检测
#define SITE_CFG_SUSPEND_TIME (3600 * 24 * 3)
#define SITE_CFG_LOG_WITH_DATA 1
//测试用, 跳过订阅信息的存储和加载
#define SITE_CFG_SKIP_SUB_SAVE 0
#define SITE_CFG_ENABLE_TRACE 0
#endif
 
using namespace std;
using namespace httplib;

using json = nlohmann::json;

namespace servicesite {

typedef struct MessageIdStruct {
    //Add in json since 2024-06-14
    string from;
    string messageId;
    string name;
    string summary;
} MessageT;

class SiteHandle;
class MessageSubscriberSiteHandle;
class MessageSubscriber;
class ServiceSiteManager;

/**
 * @brief 服务请求处理函数
 * 
 * @param 参数1 服务请求 同 http 库
 * @param 参数2 服务返回 同 http 库
 * @return int 错误码
 */
using ServiceRequestHandler = std::function<int(const Request&, Response&)>;

/**
 * @brief 消息处理函数
 * 
 * @param 参数1 消息 JSON 字符串
 * @return int 错误码
 */
using MessageHandler = std::function<void(const Request&)>;


using ServiceRequestHandlers = std::vector<std::pair<string, ServiceRequestHandler>>;
using message_t_list = std::vector<MessageT>;
using MessageHandlers = std::vector<std::pair<MessageT, MessageHandler>>;

/**
 * @brief 消息处理函数
 * 
 * @param 参数1 日志级别
 * @param 参数2 日志内容
 */
using LogHandler = std::function<void(int, string)>;

/**
 * @brief 服务站点管理器，服务站点提供相关操作支持
 * 
 */
class ServiceSiteManager {
    static const int STR_BUF_MAX_SIZE = 1024;

    ServiceRequestHandlers serviceRequestHandlers;
    message_t_list messageIds;
    MessageHandlers messageHandlers;

    std::vector<MessageSubscriber> messageSubscriberList;
    std::vector<MessageSubscriberSiteHandle*> messageSubscriberSiteHandlePList;

    static void rawHttpRequestHandler(const Request& request, Response& response);
    
    static int serviceRequestHandlerGetServiceList(const Request& request, Response& response);
    static int serviceRequestHandlerGetMessageList(const Request& request, Response& response);
    static int serviceRequestHandlerSubscribeMessage(const Request& request, Response& response);
    static int serviceRequestHandlerUnsubscribeMessage(const Request& request, Response& response);

    static int serviceRequestHandlerDebug(const Request& request, Response& response);

    static void messageHandlerRegisterAgain(const Request& request);

    string siteId;
    string summary;

    string dev_mac;

    Server server;
    int serverPort;

    std::thread pingThread;

    LogHandler log_handler = nullptr;

    int file_len(const char* filename);
    void saveMessageSubscriber(void);
    void loadMessageSubscriber(void);

    int registerSite(void);
    bool handleSubscribeMessage(string message_id, string mac, string site_id, int port, string ip);

    struct msg_queue_item_slim
    {
        MessageT msg;
        string body;
        string remote_addr;

        msg_queue_item_slim(MessageT& msg_p, const Request& req_p);
    };

    std::condition_variable message_queue_sem;
    std::queue<std::unique_ptr<msg_queue_item_slim>> message_queue;
    std::mutex message_queue_mutex;

    // 保护 初始化, 消息及其回调的注册, http回调的注册
    rwlock_holder init_mutex;
    // std::mutex http_request_mutex; // 保护 http_request handler
    // 保护 MessageSubscriberList和messageSubscriberSiteHandlePList
    rwlock_holder messageSubscriberList_mutex;

    std::thread handle_message_thread;
    static void handle_message_thread_func(void);
    void handle_mesage(std::unique_ptr<msg_queue_item_slim>& message_pair);

    int get_uid(string& mac_str);
    int get_uid_str(string& net_dev, string& mac_str);
	int get_mac(uint8_t binMAC[6], const char* net_dev);

    int popen_result(string cmd, string& result);

    ServiceSiteManager();
    static ServiceSiteManager instance;
public:
    static const int RET_CODE_OK = 0;
    static const int RET_CODE_ERROR_START_SERVER = -1;
    static const int RET_CODE_ERROR_REQ_CONN = -2;
    static const int RET_CODE_ERROR_REQ_STATUS_CODE = -3;
    static const int RET_CODE_ERROR_REQ_NOT_JSON = -4;
    static const int RET_CODE_ERROR_REQ_JSON_FORMAT = -5;
    static const int RET_CODE_ERROR_REQ_CODE = -6;

    static const string RET_OK;

    static const string QUERY_SITE_ID;
    static const string QUERY_SITE_IP;

    static const int LOG_LEVEL_INFO = 0;
    static const int LOG_LEVEL_WARN = 1;
    static const int LOG_LEVEL_ERROR = 2;

    /**
     * @brief 查询站点端口， 约定为 9000， 其他站点端口再次基础上增加
     * 
     */
    static const int QUERY_SITE_PORT = 9000;
    static const int PING_PER_SECONDS = 10;

    static const string QUERY_SITE_MESSAGE_ID_REGISTER_AGAIN;

    static const string SERVICE_ID_GET_SERVICE_LIST;
    static const string SERVICE_ID_GET_MESSAGE_LIST;
    static const string SERVICE_ID_DEBUG;
    static const string SERVICE_ID_SUBSCRIBE_MESSAGE;
    static const string SERVICE_ID_UNSUBSCRIBE_MESSAGE;

    static const string ACCESS_SERVICE_ID_GET_MESSAGE_LIST;
    static const string ACCESS_SERVICE_ID_SUBSCRIBE_MESSAGE;
    static const string ACCESS_SERVICE_ID_UNSUBSCRIBE_MESSAGE;
    static const string ACCESS_SERVICE_ID_SITE_PING;
    static const string ACCESS_SERVICE_ID_SITE_REGISTER;

    static const string MESSAGE_SUBSCRIBER_CONFIG_FILE;
    static const string MESSAGE_SUBSCRIBER_CONFIG_FILE_PATH;
    
    string messageSubscriberConfigPath;
    string self_ip;

    static ServiceSiteManager* getInstance() {
        return &instance;
    }

    static void http_exception_handler(const Request& request, Response& response, std::exception_ptr ep);

    /**
     * @brief 启动 ServiceSite 指定端口
     * 
     * @return int 错误码参照错误码定义
     */
    int start(void);

    /**
     * @brief 启动 ServiceSite 通过服务发布站点注册
     * 
     * @param pSiteId 站点 ID
     * @param pSummary 站点 简介
     * @return int 错误码参照错误码定义
     */
    int startByRegister(void);

    /**
     * @brief 注册服务请求处理函数
     * 
     * @param serviceId 服务ID
     * @param handler 服务请求处理函数指针
     * @return int 错误码参照错误码定义
     */
    int registerServiceRequestHandler(string serviceId, ServiceRequestHandler handler);

    /**
     * @brief 注册消息ID
     * 
     * @param messageId 消息ID
     * @return int int 错误码参照错误码定义
     */
    int registerMessageId(string messageId);

    /**
     * @brief 注册消息ID
     * 
     * @param messageId 消息ID
     * @param name 消息名称
     * @param summary 消息描述
     * @return int int 错误码参照错误码定义
     */
    int registerMessageId(string messageId, string name, string summary);

    /**
     * @brief 注册消息处理函数
     * 
     * @param messageId 消息ID
     * @param from_site_id 消息来自的 站点 ID
     * @param handler 消息处理函数指针
     * @return int 错误码参照错误码定义
     */
    int registerMessageHandler(string messageId, string from_site_id, MessageHandler handler);

    /**
     * @brief 发布消息
     * 
     * @param meeageId 消息ID
     * @param message 消息 JSON 字符串
     * @param is_high_priority 是否为高优先级, 默认为低优先级
     * @param msg_chk_mode 
     0(默认): 验证messageId和from是否匹配(除非为msg内部未设置/为null或空字符串), 不匹配会显示警告但不修改;
     1: 强制重写messageId和from, 用于转发(传入messageId为空时不做处理), 不匹配时不会显示警告; 
     2: 不进行任何验证, 原样传输; 
     * @return int 错误码参照错误码定义
     */
    void publishMessage(string messageId, string message, bool is_high_priority = false, int msg_chk_mode = 0);

    /**
     * @brief 获取站点服务列表
     * 
     * @param ip IP
     * @param port 端口号
     * @param to_site_id 目标站点 ID
     * @param messageIdList  获取的消息ID列表
     * @return int 错误码参照错误码定义  
     */
    int getMessageList(string ip, int port, string to_site_id, std::vector<string>& messageIdList);

    /**
     * @brief 订阅消息
     * 
     * @param ip IP
     * @param port 端口
     * @param to_site_id 目标站点 ID
     * @param messageIdList 消息ID
     * @return int 错误码参照错误码定义   
     */
    int subscribeMessage(string ip, int port, string to_site_id, std::vector<string>& messageIdList);

    /**
     * @brief 取消订阅消息
     * 
     * @param ip IP
     * @param port 端口
     * @param to_site_id 目标站点 ID
     * @param messageIdList 消息ID
     * @return int 错误码参照错误码定义   
     */
    int unsubscribeMessage(string ip, int port, string to_site_id, std::vector<string> messageIdList);

	int getServerPort() const {
		return serverPort;
	}

	void setServerPort(int serverPort) {
		this->serverPort = serverPort;
	}

    int createDir(string sPathName);

	void setMessageSubscriberConfigPath(string pMessageSubscriberConfigPath) {
		messageSubscriberConfigPath = pMessageSubscriberConfigPath;
	}

	void setSiteIdSummary(string pSiteId, string pSummary) {
		siteId = pSiteId;
		summary = pSummary;
	}

    void handle_log(int level, string file, int line, string message);

    void registerLogHandler(LogHandler handler);

    int accessServiceOjm(string ip, int port, json& request, json& response);
    int accessService(string ip, int port, json& json_request, json& json_response);
    int get_self_ip(void);

    static string get_lib_ver();

    static string dbg_tag(const string& ip, int port, const string& site_id, bool with_bracket);
};

class MessageSubscriber {
    std::vector<MessageSubscriberSiteHandle*> siteMessageSubscriberSiteHandlePlist;
public:
    string messageId;

    MessageSubscriber(string pMessageId);
    bool addSiteMessageSubscriberSiteHandleP(MessageSubscriberSiteHandle* pMessageSubscriberSiteHandle);
    void delSiteMessageSubscriberSiteHandleP(MessageSubscriberSiteHandle* pMessageSubscriberSiteHandle);
    std::vector<MessageSubscriberSiteHandle*> getSiteMessageSubscriberSiteHandlePlist(void);
};

class MessageSubscriberSiteHandle {
    static const int MAX_QUEUE_SIZE = SITE_CFG_TX_QUEUE_SIZE;
    static const int MAX_HIGH_PRIORITY_QUEUE_SIZE = SITE_CFG_TX_QUEUE_SIZE_HIGH_PRIORITY;
    static const int MAX_SEND_RETRY = SITE_CFG_MAX_SEND_RETRY;
    // 设置一个硬性最大值, 避免溢出
    static const int SEND_RETRY_HARD_LIMIT = 9999;
    // NOTE: 需要处理下时钟未正确同步的情况(以时间戳位于2020年以前为标准)
    static const uint32_t TS_YEAR_2020 = 1577836800;

    enum
    {
        STAT_DROP_IDX_CONN = 0,
        STAT_DROP_IDX_HTTP,
        STAT_DROP_IDX_FAST,
        STAT_DROP_IDX_OVERFLOW,
        STAT_DROP_IDX_SUSPEND,
        STAT_DROP_IDX_N,
    };

    Client* cli = nullptr;

    int require_fresh = 0;

    std::condition_variable queue_sem;
    std::queue<string> queue;
    std::queue<string> high_priority_queue;
    std::mutex queue_mutex; // 保护 队列, 实质上现用于保护整个handle的线程安全性
    std::thread* sendMessageThreadP;


    bool need_suspend();
    void do_suspend();
    uint32_t empty_queue();
    string dbg_tag();
    static uint32_t get_ts();

    friend class ServiceDebugResponseMessageSubscriberListItemSiteHandleListItem;

public:
    // 将其设置为负值, 以方便查看
    static const int SEND_RETRY_SUSPEND = -1;
    static const int SEND_RETRY_REFRESH = -2;

    string mac;
    string site_id;
    int port;
    string ip;

    //NOTE: 对端服务重启时, IP可能并不会发生改变, 所以我们无法区分对端服务的重启和临时订阅的消息
    //  对端本次启动时的累计次数无法计算, 而总累计次数由于包含了异常区间, 并没有实际意义
    //当前连续失败次数(发送成功时重置; 消息订阅时, 如果IP/端口发生变化也会重置)
    int retry_count = 0;
    //记录首次失败的时间戳, 用于检测及断开长期离线的设备, 在线时无意义(需要注意的是32位带符号时间戳将在2038年溢出, 无符号为2106年, 但json作为double解析时仅有52bits精度)
    uint32_t failed_ts = 0;
    //累计发送成功消息数(在此站点发起人员任意消息订阅且IP/端口改变时重置)
    uint32_t stats_cnt_send = 0;
    //累计丢弃消息数(在此站点发起人员任意消息订阅且IP/端口改变时重置, 含丢弃/失败/跳过的)
    //数组下标是STAT_DROP_IDX_*: conn, http, fast_fail, overflow, suspend
    array<uint32_t, STAT_DROP_IDX_N> stats_cnt_drop = {};

    MessageSubscriberSiteHandle(string p_mac, string p_site_id, int p_port, string p_ip);

    void sendFromThread(void);
    void sendMessage(const string& message, bool is_high_priority);
    bool refresh(string p_ip, int p_port);
};

}

#endif /* LIB_SERVICE_SITE_MANAGER_H_ */
